<!DOCTYPE HTML>
<html>

<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>Elasticsearch.Nest 教程系列 4-5 映射：Parent/Child relationships | 子父级关系映射 | DeepThought</title>
  <meta name="google-site-verification" content="Ei6mmDdf-cJwox1RvJOeX8Dc720fqWjZQu-Ww2BjBt4">
  
  <meta name="author" content="积慕">
  
  <meta name="description" content="Nest 7 教程,Nest7.x 教程,Elasticsearch.Nest,Elasticsearch.Nest 入门, NEST 入门,NEST上手, Nest 官方文档, NEST 翻译,Elasticsearch.Nest 中文,Elasticsearch.Nest 中文文档, Nest 中文文档,NET ElasticSearch,NET es,NEST 教程, Elasticsearch 客户端,Elasticsearch 7,Elasticsearch 7.x,NEST 7,NEST 7.x,automapping,自动映射,parent child,父子文档,父子关系,子父级关系文档,JointField,嵌套对象,ElasticClient.Inferrer">
  
  
  <meta name="keywords" content="Nest 7 教程,Nest7.x 教程,Elasticsearch.Nest,Elasticsearch.Nest 入门, NEST 入门,NEST上手, Nest 官方文档, NEST 翻译,Elasticsearch.Nest 中文,Elasticsearch.Nest 中文文档, Nest 中文文档,NET ElasticSearch,NET es,NEST 教程, Elasticsearch 客户端,Elasticsearch 7,Elasticsearch 7.x,NEST 7,NEST 7.x,automapping,自动映射,parent child,父子文档,父子关系,子父级关系文档,JointField,嵌套对象,ElasticClient.Inferrer">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
  <meta property="og:title" content="Elasticsearch.Nest 教程系列 4-5 映射：Parent/Child relationships | 子父级关系映射">
  <meta property="og:site_name" content="DeepThought">

  
  <meta property="og:image" content>
  

  <link href="https://pic.zhuliang.ltd/favicon.ico" rel="shortcut icon" type="image/x-icon">
  <link rel="alternate" href="/atom.xml" title="DeepThought" type="application/atom+xml">
  <link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">

  <link rel="stylesheet" href="//at.alicdn.com/t/font_1187293_8fec2ou5fyx.css">

  <!-- <link rel="stylesheet" href="//cdn.bootcss.com/highlight.js/9.6.0/styles/github.min.css"> -->
  <!-- <script src="//cdn.bootcss.com/highlight.js/9.6.0/highlight.min.js"></script> -->
  <script src="/js/highlight.min.js"></script>

  <link rel="stylesheet" href="/css/markdown.css">

  <script src="/js/av-min.3.0.4.js"></script>
  <script src="/js/Valine.min.1.3.6.js"></script>

</head></html>

<body>
  <header id="header" class="inner"><div class="alignleft">
  <h1><a href="/">DeepThought</a></h1>
  <h2><a href="/">知识需沉淀，更需分享</a></h2>
</div>
<nav id="main-nav" class="alignright">
  <ul>
    
      <li><a href="/">首页</a></li>
    
      <li><a href="/about">关于我</a></li>
    
      <li><a href="/atom.xml">订阅RSS</a></li>
    
  </ul>
  <div class="clearfix"></div>
</nav>
<div class="clearfix"></div>
        </header>
  <div id="content" class="inner">
    <div id="main-col" class="alignleft"><div id="wrapper"><article class="post">
  
  <div class="post-content">
    <header>
      
        <div class="icon"></div>
      

 
      
  
    <h1 class="title">Elasticsearch.Nest 教程系列 4-5 映射：Parent/Child relationships | 子父级关系映射</h1>
  


      
        <time datetime="2020-01-16T08:27:01.000Z">
          create: 2020-01-16 16:27:01 | update: 2020-01-23 12:15:44
            <span style="float: right;">
    <span class="post-count" id="busuanzi_container_page_pv" style="display: inline;">
        
        本文总阅读量: <span id="busuanzi_value_page_pv"></span>&nbsp;次
        <span class="blueSeparate">&nbsp;|&nbsp;</span>
        
        <span class="post-count">文章总字数:&nbsp;2.4k&nbsp;字</span>
    <span class="blueSeparate">&nbsp;|&nbsp;</span>
    <span class="post-count">阅读约需:&nbsp;10&nbsp;分钟</span>
    <br>
</span>
</span>
        </time>
      
            
    </header>
    <div class="entry">
      
      <blockquote>
<ul>
<li>本系列博文是“伪”官方文档翻译（更加本土化），并非完全将官方文档进行翻译，而是在查阅、测试原始文档并转换为自己真知灼见后的“准”翻译。有不同见解 / 说明不周的地方，还请海涵、不吝拍砖 ：）</li>
<li>官方文档见此：<a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html" target="_blank" rel="noopener">https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html</a></li>
<li>本系列对应的版本环境：<a href="mailto:ElasticSearch@7.3.1" target="_blank" rel="noopener">ElasticSearch@7.3.1</a>，<a href="mailto:NEST@7.3.1" target="_blank" rel="noopener">NEST@7.3.1</a>，IDE 和开发平台默认为 VS2019，.NET CORE 2.1</li>
</ul>
</blockquote>
<hr>
<blockquote>
<p><strong>如果把 <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/parent-child-relationships.html" target="_blank" rel="noopener">官方示例</a> 放在你本地进行测试，会发现没有问题，但当你连接测试 ES 服务器的时候，就会报各种错误：</strong></p>
<ul>
<li>这是因为官方使用了 InMemoryConnection ，所有请求并不会真实发送到测试 ES 服务器，规避掉了不少现实问题。以下示例基于官方文档进行了修正，使用本地搭建测试 ES 服务器。 </li>
</ul>
</blockquote>
<p>父子关系的数据结构，在日常开发过程中，使用还是比较平凡的：</p>
<ul>
<li>在 ES 6.x 之前，你可以在一个索引中包含多种 type(类型)。通过给定类型的特殊 _parent 字段映射，可以创建 1对N 关系 的父-子文档。<ul>
<li>通过这种方式在索引子项时，你需要传递一个 _parent id 作为路由键（<strong>需要确保父项、其子项及所有祖先都保存在同一个分片上</strong>）。</li>
</ul>
</li>
<li>但从 ES 6.x 开始，type 不再支持多类型，固定  _type=_doc。（另外在 ES5 的时候，ES 的 index 相当于 DB，_type 相当于 DB 中的表名，但从 ES7 开始，ES 的 index 相当于 DB中的表，_type 固定为 _doc）</li>
</ul>
<p>嵌套对象和父子结构文档的差异对比和使用建议：</p>
<table>
<thead>
<tr>
<th></th>
<th>Nested Object</th>
<th>Parent/Child</th>
</tr>
</thead>
<tbody>
<tr>
<td>优点</td>
<td>文档存储再一起，读取性能高</td>
<td>父子文档可以独立更新</td>
</tr>
<tr>
<td>缺点</td>
<td>更新嵌套的子文档时，需要更新整个文档</td>
<td>需要额外的内存维护关系。读取性能相对差</td>
</tr>
<tr>
<td>适用场景</td>
<td>子文档偶尔更新，以查询为主</td>
<td>子文档更新频繁</td>
</tr>
</tbody>
</table>
<p>既然索引不再允许将不同 _type 类型存储在同一索引中，那么如何创建父联接？</p>
<a id="more"></a>
<h1 id="通过-JoinField-属性"><a href="#通过-JoinField-属性" class="headerlink" title="通过 JoinField 属性"></a>通过 JoinField 属性</h1><blockquote>
<p>唯一的要求是作为子父级关系的类必须要具有数据类型为 JoinField 的属性。通过该属性，在请求的时候，可以生成关联类型。</p>
</blockquote>
<p>示例：</p>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">public abstract class MyDocument
{
    public int Id { get; set; }
    public JoinField ParentChildRelation { get; set; }
}

public class MyParent : MyDocument
{
    [Text]
    public string ParentProperty { get; set; }
}
public class MyChild : MyDocument
{
    [Text]
    public string ChildProperty { get; set; }
}
</code></pre>
<ul>
<li>MyParent 和 MyChild 均继承自 MyDocument，且注意 MyDocument.ParentChildRelation  的数据类型为 JoinField （该类型是 Nest 库提供的数据类型） 。<ul>
<li>这里，parentChildRelation 即为 Join 类型的名称。</li>
</ul>
</li>
</ul>
<p><strong>将 MyChild 和 MyParent 设置相同的索引和默认文档类型，以确保它们在同一个索引中：</strong></p>
<ul>
<li>需要将 MyDocument，MyChild 和 MyParent 的索引名称都指定为 “index”确保它们在同一个索引中。</li>
<li>DefaultMappingFor 提供了 RelationName ，在 MyParent 上 指定子父级的关联名称为 “parent”。</li>
</ul>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">public ElasticSearchClient(ElasticSearchSettings esSettings)
{
    _esSettings = esSettings;
    var settings = new ConnectionSettings(new Uri(_esSettings.ServerUri))
    .DefaultMappingFor&lt;MyDocument&gt;(m =&gt; m.IndexName(&quot;index&quot;))
    .DefaultMappingFor&lt;MyChild&gt;(m =&gt; m.IndexName(&quot;index&quot;))
    .DefaultMappingFor&lt;MyParent&gt;(m =&gt; m.IndexName(&quot;index&quot;).RelationName(&quot;parent&quot;)) //你也可以通过 RelationName 来设定关系名，最终会体现在创建索引的请求上
    .EnableDebugMode();

    _client = new ElasticClient(settings);
}
</code></pre>
<p><strong>通过设置 ConnectionStrings，可以将 MyParent 和 MyChild 映射为索引的一部分，并需要确保子父级文档在同一个分片上：</strong></p>
<ul>
<li>在创建“索引”的时候，通过将“_route”的 required 设置为 true，确保子父级文档在同一个分片上。<ul>
<li>在同一个分片确保了 JOIN 的查询性能。</li>
<li>关于 Nest 中的路由推断，见此博文：<a href="https://blog.zhuliang.ltd/2020/01/backend/Elasticsearch-Nest-Routing-inference.html">NEST 教程系列 9-8 转换：Routing Inference | 路由推断</a></li>
</ul>
</li>
</ul>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">var createIndexResponse = client.Indices.Create(&quot;index&quot;, c =&gt; c
    .Index&lt;MyDocument&gt;()
    .Map&lt;MyDocument&gt;(m =&gt; m
        .RoutingField(r =&gt; r.Required())  //1.加上 RoutingField，以保证父子文档被分配到同一个分片上
        .AutoMap&lt;MyParent&gt;()            //2.自动映射所有 MyParent 属性
        .AutoMap&lt;MyChild&gt;()             //3.自动映射所有 MyChild 属性
        .Properties(props =&gt; props
            .Join(j =&gt; j                        //4.AutoMap 不会映射 JoinField，需要手动指定
                .Name(p =&gt; p.MyJoinField)  // 标记A
                .Relations(r =&gt; r
                    .Join&lt;MyParent, MyChild&gt;()  //因为 ConnectionSettings 指定了 MyParent 的 RelationName 为 parent，所以这里在映射父/子关系的时候，父类型的名称为 “parent”，而 子类顶的名称会自动反射为类的完整名称（即包含了命名空间）。
                )
            )
        )
    )
);
</code></pre>
<ul>
<li>因为 NEST 的 AutoMap 方法不会自动配置 JoinField 映射，所以你需要手动装配。</li>
<li>以上装配方式将 MyChild 设置为 MyParent 的子集。.Join() 方法有许多重载，你可以按需进行选择。</li>
</ul>
<p>以上命令相当于执行了下面的 ES 命令：</p>
<p></p><p class="code-caption" data-lang="sh" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="sh">PUT /index
{
    &quot;mappings&quot;: {
        &quot;properties&quot;: {
            &quot;childProperty&quot;: {
                &quot;type&quot;: &quot;text&quot;
            },
            &quot;id&quot;: {
                &quot;type&quot;: &quot;integer&quot;
            },
            &quot;myJoinField&quot;: {
                &quot;relations&quot;: {
                    &quot;parent&quot;: &quot;mychild&quot; 
                },
                &quot;type&quot;: &quot;join&quot;
            },
            &quot;parentProperty&quot;: {
                &quot;type&quot;: &quot;text&quot;
            }
        },
        &quot;_routing&quot;: {
            &quot;required&quot;: true
        }
    }
}
</code></pre>
<p><img src="https://pic.zhuliang.ltd/20200117141011.png-c" alt></p>
<ul>
<li>在 ConnectionString 上设置映射的时候，把 MyParent 的关系名称设置为了“parent”。稍后在执行强类型的 has_child 和 has_parent 查询时，这也很方便。</li>
</ul>
<h1 id="添加父、子文档到-ES-中"><a href="#添加父、子文档到-ES-中" class="headerlink" title="添加父、子文档到 ES 中"></a>添加父、子文档到 ES 中</h1><h2 id="添加父文档到-ES-中"><a href="#添加父文档到-ES-中" class="headerlink" title="添加父文档到 ES 中"></a>添加父文档到 ES 中</h2><p>通过 MyParent 的关系名称（”parent”）标记文档，以下三种方式都是等效的：</p>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">var parentDocument = new MyParent
{
    Id = 1,
    ParentProperty = &quot;a parent prop&quot;,
    MyJoinField = JoinField.Root&lt;MyParent&gt;() // 指明是 MyParent 类的跟文档(会自动对应到&quot;parent&quot;)
};

parentDocument = new MyParent
{
    Id = 1,
    ParentProperty = &quot;a parent prop&quot;,
    MyJoinField = typeof(MyParent) // 指明是 MyParent 类的跟文档(会自动对应到&quot;parent&quot;)
};

parentDocument = new MyParent
{
    Id = 1,
    ParentProperty = &quot;a parent prop&quot;,
    MyJoinField = &quot;parent&quot;   //可以直接使用在 ConnectionSettings 配置的字段。
};

/* 以下官方示例有问题，虽然作者说包含了 JointField 属性的类不需要再显式指定 routing，但实际测试下来，并非如此---这是一个bug，从 issues 上看已经延续了3年之久，至今未修复。

至于示例文档为什么能跑通且不报错，是因为示例文档并非真实请求---使用了 InMemoryConnection。

// 因为开启了 routing，所以在请求的时候，请求 URL 上需要指定 routing，而以下索引方式并不会添加 ?routing
var indexParent = client.IndexDocument(parentDocument);

*/

//使用 Index 方法来进行索引文档
var indexParent = _client.Index(parentDocument, desc =&gt;
{
    desc.Routing(parentDocument.Id); //显式指定 Routing 的值
    return desc;
});
</code></pre>
<p>最终生成的请求命令 为：</p>
<p></p><p class="code-caption" data-lang="sh" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="sh">PUT /index/_doc/1?routing=1
{
    &quot;parentProperty&quot;: &quot;a parent prop&quot;,
    &quot;id&quot;: 1,
    &quot;parentChildRelation&quot;: &quot;parent&quot;
}
</code></pre>
<h2 id="添加子文档到-ES-中"><a href="#添加子文档到-ES-中" class="headerlink" title="添加子文档到 ES 中"></a>添加子文档到 ES 中</h2><p><strong>在添加子文档的时候必须指定它的父文档id：</strong></p>
<ul>
<li>使用 route 参数保证父子文档被分配到相同的分片上。</li>
</ul>
<p>依葫芦画瓢，将子文档连接到它的父文档上。这里直接使用上面的 parentDocument 对象 来创建连接：</p>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">/* 同样这里不能使用官方通过 IndexDocument 来索引文档的例子
var indexChild = client.IndexDocument(new MyChild
{
    MyJoinField = JoinField.Link&lt;MyChild, MyParent&gt;(parentDocument)
});
*/

//使用 Index 来代替 IndexDocument 以可以显式指定 routing
var child = new MyChild
{
    Id = 10002,
    ChildProperty = &quot;a child prop&quot;,
    ParentChildRelation = JoinField.Link&lt;MyChild, MyParent&gt;(parentDocument)
    //使用该重载也是可以的 ParentChildRelation = JoinField.Link&lt;MyChild&gt;(1)
};
var indexChild = _client.Index(child, desc =&gt;
{
    desc.Routing(parentDocument.Id);  //显式指定 routing，同 parentid
    return desc;
});
</code></pre>
<p>生成的请求命令为：</p>
<p></p><p class="code-caption" data-lang="sh" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="sh">PUT /index/_doc/10002?routing=1
{
    &quot;childProperty&quot;: &quot;a child prop&quot;,
    &quot;id&quot;: 10002,
    &quot;parentChildRelation&quot;: {
        &quot;name&quot;: &quot;mychild&quot;,
        &quot;parent&quot;: &quot;1&quot;
    }
}
</code></pre>
<p>最终在 index 索引中，生成的父子文档内容如下：</p>
<p></p><p class="code-caption" data-lang="json" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="json">{
    &quot;hits&quot; : [
      {
        &quot;_index&quot; : &quot;index&quot;,
        &quot;_type&quot; : &quot;_doc&quot;,
        &quot;_id&quot; : &quot;1&quot;,
        &quot;_score&quot; : 1.0,
        &quot;_routing&quot; : &quot;1&quot;,
        &quot;_source&quot; : {
          &quot;parentProperty&quot; : &quot;a parent prop&quot;,
          &quot;id&quot; : 1,
          &quot;parentChildRelation&quot; : &quot;parent&quot;
        }
      },
      {
        &quot;_index&quot; : &quot;index&quot;,
        &quot;_type&quot; : &quot;_doc&quot;,
        &quot;_id&quot; : &quot;10002&quot;,
        &quot;_score&quot; : 1.0,
        &quot;_routing&quot; : &quot;1&quot;,
        &quot;_source&quot; : {
          &quot;childProperty&quot; : &quot;a child prop&quot;,
          &quot;id&quot; : 10002,
          &quot;parentChildRelation&quot; : {
            &quot;name&quot; : &quot;mychild&quot;,
            &quot;parent&quot; : &quot;1&quot;
          }
        }
      }
    ]
}
</code></pre>
<h1 id="ElasticClient-Inferrer"><a href="#ElasticClient-Inferrer" class="headerlink" title="ElasticClient.Inferrer"></a>ElasticClient.Inferrer</h1><ul>
<li>在索引的时候，需要指定 routing</li>
<li>在过去，你需要在请求中使用 parent=<parentid> 的方式（在 ES 5.x），或者 routing=<parentid> 的方式（在 ES 6.x）来路由父子文档，在 Nest 7 中， Nest 提供了一个帮助类来推断正确的路由值，它会根据 JoinField 属性来推断父类。</parentid></parentid></li>
</ul>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">var infer = _client.Infer;
var parentInfo = new MyParent
{
    Id = 2,
    ParentProperty = &quot;a parent prop 2&quot;,
    ParentChildRelation = JoinField.Root&lt;MyParent&gt;()
};
var parentRoute = infer.Routing(parentInfo); //结果为父文档id：2

var child = new MyChild
{
    Id = 20002,
    ChildProperty = &quot;a child prop of P2&quot;,
    ParentChildRelation = JoinField.Link&lt;MyChild, MyParent&gt;(parentInfo)
};
var childRoute = infer.Routing(child);   //结果为 父文档id：2
</code></pre>
<p>通过 Routing 方法来改写上面索引父子文档的例子：</p>
<p></p><p class="code-caption" data-lang="csharp" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="csharp">//索引父文档
var parentInfo = new MyParent
{
    Id = 2,
    ParentProperty = &quot;a parent prop 2&quot;,
    ParentChildRelation = JoinField.Root&lt;MyParent&gt;()
};
var parentResponse = _client.Index(parentInfo, i =&gt; i.Routing(Routing.From(parentInfo)));

//索引子文档
var child = new MyChild
{
    Id = 20002,
    ChildProperty = &quot;a child prop of P2&quot;,
    ParentChildRelation = JoinField.Link&lt;MyChild, MyParent&gt;(parentInfo)
};
var childResponse = _client.Index(child, i =&gt; i.Routing(Routing.From(child)));
</code></pre>
<p>最终添加的文档内容为：</p>
<p></p><p class="code-caption" data-lang="json" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="json">{
    &quot;hits&quot; : [
          {
            &quot;_index&quot; : &quot;index&quot;,
            &quot;_type&quot; : &quot;_doc&quot;,
            &quot;_id&quot; : &quot;2&quot;,
            &quot;_score&quot; : 1.0,
            &quot;_routing&quot; : &quot;2&quot;,
            &quot;_source&quot; : {
              &quot;id&quot; : 2,
              &quot;parentProperty&quot; : &quot;a parent prop 2&quot;,
              &quot;parentChildRelation&quot; : &quot;parent&quot;
            }
          },
          {
            &quot;_index&quot; : &quot;index&quot;,
            &quot;_type&quot; : &quot;_doc&quot;,
            &quot;_id&quot; : &quot;20002&quot;,
            &quot;_score&quot; : 1.0,
            &quot;_routing&quot; : &quot;2&quot;,
            &quot;_source&quot; : {
              &quot;id&quot; : 20002,
              &quot;childProperty&quot; : &quot;a child prop of P2&quot;,
              &quot;parentChildRelation&quot; : {
                &quot;name&quot; : &quot;mychild&quot;,
                &quot;parent&quot; : &quot;2&quot;
              }
            }
          }
    ]
}
</code></pre>

      

      <div>
        
        <div style="background-color: rgb(255, 23, 0);user-select: auto;height: 3px;width:100%;margin-top: 40px;margin-bottom:15px;"></div>

<!-- <p>常言道：学然后知不足，教然后知困。</p>
<p>我知道你的焦虑，一起共进加油：P</p>
<p>关不关注都无所谓，会根据生活节奏紧凑度定期分享些开发经验、搬砖生涯、痛点、感悟。</p>
<p>
        <img src="https://pic.zhuliang.ltd/20191213133908.gif" alt="欢迎关注我的订阅号：P" />
</p> -->
<ul class="post-copyright" style="margin-left:0px;">
    <li class="post-copyright-author">
        <strong>本文作者：</strong>积慕
    </li>
    <li class="post-copyright-link">
        <strong>本文链接：</strong>
        <a href="/2020/01/backend/Elasticsearch-Nest-parent-child-relationships.html" title="Elasticsearch.Nest 教程系列 4-5 映射：Parent/Child relationships | 子父级关系映射">https://blog.zhuliang.ltd/2020/01/backend/Elasticsearch-Nest-parent-child-relationships.html</a>
    </li>
    <li class="post-copyright-license">
        <strong>版权： </strong>
        本站文章均采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/cn/" rel="external nofollow"
            target="_blank">CC BY-NC-SA 3.0 CN</a> 许可协议，请勿用于商业，转载注明出处！
    </li>
</ul>
        
      </div>
    </div>

    <footer>
      
      
  
  <div class="categories">
    <a href="/categories/backend/">backend</a>
  </div>

      
  
  <div class="tags">
    <a href="/tags/教程/">教程</a>, <a href="/tags/Elasticsearch-Nest系列/">Elasticsearch.Nest系列</a>, <a href="/tags/Elasticsearch/">Elasticsearch</a>
  </div>

      <!-- 
  <div class="addthis addthis_toolbox addthis_default_style">
    
    
      <a class="addthis_button_tweet"></a>
    
    
      <a class="addthis_button_google_plusone" g:plusone:size="medium"></a>
    
    
      <a class="addthis_button_pinterest_pinit" pi:pinit:layout="horizontal"></a>
    
    <a class="addthis_counter addthis_pill_style"></a>
  </div>
  <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js"></script>
 -->
      
      <div class="clearfix"></div>
    </footer>
  </div>
</article>

<!-- 
<section id="comment">
  <h1 class="title">留言</h1>

  <div id="vcomments" style="background-color: #fff"></div>
  <script>
    new Valine({
      el: '#vcomments',
      appId: 'aTPKmG6tjDo3CbIXzAPzrj4X-gzGzoHsz',
      appKey: 'hNgWFneNpEcMnBJclPQnDgsd',
      notify: true,
      verify: true, //验证码
      avatar: 'monsterid', //https://valine.js.org/avatar.html
      placeholder: '请输入你的评论'
    })
  </script>
</section>
 --> <!--启用/关闭 LeanCloud 评论--></div></div>
    <aside id="sidebar" class="alignright">
  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-category"></i>&nbsp;&nbsp;分类</h3>
  <ul class="entry">
  
    <li><a href="/categories/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/categories/PowerUser/">PowerUser</a><small>4</small></li>
  
    <li><a href="/categories/backend/">backend</a><small>57</small></li>
  
    <li><a href="/categories/frontend/">frontend</a><small>9</small></li>
  
    <li><a href="/categories/operations/">operations</a><small>5</small></li>
  
    <li><a href="/categories/提前退休/">提前退休</a><small>2</small></li>
  
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-new3"></i>&nbsp;&nbsp;最新文章</h3>
  <ul class="entry">
    
      <li>
        <a href="/2024/11/PowerUser/install-docker-in-pve-lxc.html">1. 通过 PVE LXC 搭建 docker 环境</a>
      </li>
    
      <li>
        <a href="/2024/11/Obsidian/obsidian-render-pie-chart.html">2. 2 套用于 obsidian 的生成时间占用饼图脚本示例，基于 templater， charts，echarts+dataview</a>
      </li>
    
      <li>
        <a href="/2024/10/PowerUser/fix-ios18-samba-readonly-issue.html">3. 解决 ios 18（包括ipadOS 18） 下通过文件(files app)连接 samba 服务器，目录变成只读权限的问题</a>
      </li>
    
      <li>
        <a href="/2023/07/operations/how-to-use-pm2.html">4. PM2 的安装、配置和使用</a>
      </li>
    
      <li>
        <a href="/2023/07/backend/expose-intranet-netcoreapi-to-internet.html">5. 通过 DDNS+显性URL 把内网部署的.NET CORE WEBAPI 项目暴露到公网中</a>
      </li>
    
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-tag"></i>&nbsp;&nbsp;标签</h3>
  <ul class="entry">
  
    <li><a href="/tags/net-core/">.net core</a><small>12</small></li>
  
    <li><a href="/tags/Debian/">Debian</a><small>2</small></li>
  
    <li><a href="/tags/Docker/">Docker</a><small>1</small></li>
  
    <li><a href="/tags/Elasticsearch/">Elasticsearch</a><small>35</small></li>
  
    <li><a href="/tags/Elasticsearch-Nest系列/">Elasticsearch.Nest系列</a><small>35</small></li>
  
    <li><a href="/tags/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/tags/PVE/">PVE</a><small>2</small></li>
  
    <li><a href="/tags/StackExchange-Redis系列/">StackExchange.Redis系列</a><small>12</small></li>
  
    <li><a href="/tags/centos/">centos</a><small>3</small></li>
  
    <li><a href="/tags/flex/">flex</a><small>1</small></li>
  
    <li><a href="/tags/ios/">ios</a><small>1</small></li>
  
    <li><a href="/tags/javascript/">javascript</a><small>1</small></li>
  
    <li><a href="/tags/linux/">linux</a><small>4</small></li>
  
    <li><a href="/tags/nginx/">nginx</a><small>2</small></li>
  
    <li><a href="/tags/nuget/">nuget</a><small>2</small></li>
  
    <li><a href="/tags/react/">react</a><small>6</small></li>
  
    <li><a href="/tags/styled-components/">styled-components</a><small>1</small></li>
  
    <li><a href="/tags/内网穿透/">内网穿透</a><small>4</small></li>
  
    <li><a href="/tags/培训讲义/">培训讲义</a><small>2</small></li>
  
    <li><a href="/tags/基础知识/">基础知识</a><small>2</small></li>
  
    <li><a href="/tags/性能优化/">性能优化</a><small>2</small></li>
  
    <li><a href="/tags/教程/">教程</a><small>64</small></li>
  
    <li><a href="/tags/理财/">理财</a><small>2</small></li>
  
    <li><a href="/tags/生命周期/">生命周期</a><small>1</small></li>
  
    <li><a href="/tags/经验/">经验</a><small>4</small></li>
  
  </ul>
</div>


  
  <script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
  <script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
  <div class="widget tag">
    <h3 class="title"><i class="iconfont icon-cloud1"></i>&nbsp;&nbsp;标签云</h3>
    <div id="myCanvasContainer" class="widget tagcloud">
      <canvas width="250" height="250" id="resCanvas" style="width:100%">
        <a href="/tags/net-core/" style="font-size: 17.14px;">.net core</a> <a href="/tags/Debian/" style="font-size: 11.43px;">Debian</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/Elasticsearch/" style="font-size: 18.57px;">Elasticsearch</a> <a href="/tags/Elasticsearch-Nest系列/" style="font-size: 18.57px;">Elasticsearch.Nest系列</a> <a href="/tags/Obsidian/" style="font-size: 10px;">Obsidian</a> <a href="/tags/PVE/" style="font-size: 11.43px;">PVE</a> <a href="/tags/StackExchange-Redis系列/" style="font-size: 17.14px;">StackExchange.Redis系列</a> <a href="/tags/centos/" style="font-size: 12.86px;">centos</a> <a href="/tags/flex/" style="font-size: 10px;">flex</a> <a href="/tags/ios/" style="font-size: 10px;">ios</a> <a href="/tags/javascript/" style="font-size: 10px;">javascript</a> <a href="/tags/linux/" style="font-size: 14.29px;">linux</a> <a href="/tags/nginx/" style="font-size: 11.43px;">nginx</a> <a href="/tags/nuget/" style="font-size: 11.43px;">nuget</a> <a href="/tags/react/" style="font-size: 15.71px;">react</a> <a href="/tags/styled-components/" style="font-size: 10px;">styled-components</a> <a href="/tags/内网穿透/" style="font-size: 14.29px;">内网穿透</a> <a href="/tags/培训讲义/" style="font-size: 11.43px;">培训讲义</a> <a href="/tags/基础知识/" style="font-size: 11.43px;">基础知识</a> <a href="/tags/性能优化/" style="font-size: 11.43px;">性能优化</a> <a href="/tags/教程/" style="font-size: 20px;">教程</a> <a href="/tags/理财/" style="font-size: 11.43px;">理财</a> <a href="/tags/生命周期/" style="font-size: 10px;">生命周期</a> <a href="/tags/经验/" style="font-size: 14.29px;">经验</a>
      </canvas>
    </div>
  </div>




</aside>
    <div class="clearfix"></div>
  </div>
  <footer id="footer" class="inner"><div class="alignleft">

  
  &copy; 2018 - 2024 &nbsp;<a href='/about'>积慕</a>
  
  &nbsp;&nbsp;<a href='https://www.cnblogs.com/deepthought/'>我的博客园地址</a>
  &nbsp;&nbsp;finalclassic#outlook.com
</div>

<div class="clearfix"></div>
<div>
  本站主题基于：<a href='https://github.com/hexojs/hexo-theme-light' rel="external nofollow">hexo-theme-light</a>
</div>
<div id="busuanzi_container_site_pv">
  本站总访问量&nbsp;<span id="busuanzi_value_site_pv"></span>&nbsp;次
</div>
<div style="width:300px;margin:0 auto; padding:20px 0;">
  <a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=31011702004805" style="display:inline-block;text-decoration:none;height:20px;line-height:20px;">
    <img src="https://pic.zhuliang.ltd/20200102164648.png" style="float:left;"><p style="float:left;height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;">沪公网安备 31011702004805号</p></a>
</div></footer>
  <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>




<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
(function($){
  $('.fancybox').fancybox();
})(jQuery);
</script>


<script type="text/javascript">
hljs.initHighlightingOnLoad();
</script>

<!-- <script src="/js/busuanzi.pure.mini.js"></script> -->
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<script>
    var _hmt = _hmt || [];
    (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ea793b9c8d6b31df666a6bc15038e246";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
    })();
</script>
<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        }
        else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>
</body>
</html>
